package com.kongjs.application.tenant;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.kongjs.application.model.entity.Permissions;
import com.kongjs.application.model.entity.RolePermission;
import com.kongjs.application.model.entity.Roles;
import com.kongjs.application.model.entity.Tenants;
import com.kongjs.application.service.*;
import com.kongjs.common.mybatis.tenant.TenantContextHolder;
import com.kongjs.common.security.component.DynamicRoleHierarchy;
import com.kongjs.common.security.util.TreesUtil;
import jakarta.annotation.Resource;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class TenantRoleHierarchyInitializerImpl implements TenantRoleHierarchyInitializer {
    @Resource
    private DynamicRoleHierarchy dynamicRoleHierarchy;
    @Resource
    private TenantRoleHierarchy tenantRoleHierarchy;
    @Resource
    private ITenantService tenantService;
    @Resource
    private IRolesService rolesService;
    @Resource
    private IRoleHierarchyService roleHierarchyService;
    @Resource
    private IRolePermissionService rolePermissionService;
    @Resource
    private IPermissionsService permissionsService;

    @Override
    public void initRoleHierarchy() {
        String tenantId = TenantContextHolder.getTenantId();
        initRoleHierarchy(tenantId);
    }

    @Override
    public void initRoleHierarchy(String tenantId) {
        TenantContextHolder.setTenantId(tenantId);
        try {
            List<Roles> roles = rolesService.lambdaQuery().eq(Roles::getEnabled, 1).list();
            List<Permissions> permissions = permissionsService.lambdaQuery().eq(Permissions::getEnabled, 1).list();
            List<RolePermission> rolePermissions = rolePermissionService.lambdaQuery().list();
            Map<Long, Roles> rolesMap = new HashMap<>();
            for (Roles role : roles) {
                rolesMap.put(role.getId(), role);
            }
            Map<Long, Permissions> permissionsMap = new HashMap<>();
            for (Permissions permission : permissions) {
                permissionsMap.put(permission.getId(), permission);
            }
            Map<Long, List<Long>> rolePermissionMap = rolePermissions.stream()
                    .collect(Collectors.groupingBy(
                            RolePermission::getRoleId,
                            Collectors.mapping(RolePermission::getPermissionId, Collectors.toList()))
                    );
            List<TreeNode<Long>> roleTreeNodes = new ArrayList<>();
            for (Roles role : roles) {
                TreeNode<Long> node = new TreeNode<>();
                node.setId(role.getId());
                node.setParentId(role.getParentId());
                node.setName(role.getAuthority());
                roleTreeNodes.add(node);
                List<Long> permissionIds = rolePermissionMap.get(role.getId());
                if (!CollectionUtils.isEmpty(permissionIds)) {
                    for (Long permissionId : permissionIds) {
                        Permissions permission = permissionsMap.get(permissionId);
                        if (ObjectUtils.isEmpty(permission)) {
                            continue;
                        }
                        TreeNode<Long> childNode = new TreeNode<>();
                        childNode.setId(permission.getId());
                        childNode.setParentId(role.getId());
                        childNode.setName(permission.getAuthority());
                        roleTreeNodes.add(childNode);
                    }
                }
            }
            if (!CollectionUtils.isEmpty(roleTreeNodes)) {
                // 角色树
                List<Tree<Long>> roleTrees = TreeUtil.build(roleTreeNodes, null);
                Map<Long, List<Tree<Long>>> map = TreesUtil.getAllRecursiveChildrenMap(roleTrees);
                Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = new HashMap<>();
                for (Map.Entry<Long, List<Tree<Long>>> entry : map.entrySet()) {
                    Roles role = rolesMap.get(entry.getKey());
                    if (role == null || entry.getValue().isEmpty()) {
                        continue;
                    }
                    // System.out.println(role.getAuthority() + " " + entry.getValue().stream().map(Tree::getName).toList());
                    Set<GrantedAuthority> authorities = entry.getValue()
                            .stream()
                            .map(t -> new SimpleGrantedAuthority(t.getName().toString()))
                            .collect(Collectors.toSet());
                    rolesReachableInOneOrMoreStepsMap.put(role.getAuthority(), authorities);
                }
                DynamicRoleHierarchy dynamicRoleHierarchy = new DynamicRoleHierarchy();
                dynamicRoleHierarchy.setRolesReachableInOneOrMoreStepsMap(rolesReachableInOneOrMoreStepsMap);
                tenantRoleHierarchy.saveRoleHierarchy(tenantId, dynamicRoleHierarchy);
            }
        } finally {
            TenantContextHolder.clearTenantId();
        }
    }

    @Override
    public void initAllRoleHierarchy() {
        List<Tenants> tenants = tenantService.lambdaQuery().eq(Tenants::getEnabled, 1).list();
        for (Tenants tenant : tenants) {
            initRoleHierarchy(String.valueOf(tenant.getId()));
        }
    }
}
